package top.someapp.applib.kits;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import top.someapp.applib.CommonCharsets;
import top.someapp.applib.SpecialTexts;

/**
 * @author zw-zheng
 * Created on 2019-07-19
 */
public class Strings {

    public static final Charset CHARSET_UTF8 = CommonCharsets.UTF_8;
    public static final Charset CHARSET_GBK = CommonCharsets.GBK;
    public static final char EMPTY_CHAR = SpecialTexts.EMPTY_CHAR;
    private static final Pattern placeHolder = Pattern.compile("(\\s\\S)*(<>)(\\s\\S)*");
    //    private static final Pattern templateVarName = Pattern.compile("\\{([A-Za-z_]+?)\\}");
    private static final Pattern getterReg = Pattern.compile("^(get|is)[A-Z]+\\S*");
    private static final Pattern setterReg = Pattern.compile("^(set)[A-Z]+\\S*");
    private static final Pattern upperPrefix = Pattern.compile("([A-Z]+)\\S*");
    private static final Pattern lowerPrefix = Pattern.compile("([a-z]+)\\S*");

    // read methods
    public static String readAsStringUTF8(InputStream ins) {
        return readAsString(ins, CHARSET_UTF8);
    }

    public static String readAsString(InputStream ins, Charset charset) {
        byte[] bytes = new byte[512];
        ByteArrayOutputStream out = new ByteArrayOutputStream(512);
        try {
            for (int len; ; ) {
                len = ins.read(bytes);
                if (len < 1) {
                    break;
                }
                out.write(bytes, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
        return charset == null ? new String(out.toByteArray())
            : new String(out.toByteArray(), charset);
    }
    // ~read methods

    // check and test

    /**
     * str是否是一个可见的字符串
     *
     * @param str 待测试的字符串
     * @return true：str可见；false：str不可见
     */
    public static boolean isVisible(String str) {
        return str != null && str.trim().length() > 0;
    }

    /**
     * str是否是一个不可见的字符串
     *
     * @param str 待测试的字符串
     * @return true：str不可见；false：str可见
     */
    public static boolean isNotVisible(String str) {
        return !isVisible(str);
    }

    /**
     * str是否为null或空白
     *
     * @param str 待测试的字符串
     * @return true：str为null或空白；false：str不为空也不是空白
     */
    public static boolean isNullOrEmpty(String str) {
        return str == null || str.length() == 0;
    }

    /**
     * s1和s2的文本内容是否一样，null 和 "" 认为是一样的
     *
     * @param s1 第一个字符串
     * @param s2 第二个字符串
     * @return true：s1和s2的文本内容一样；false：不一样
     */
    public static boolean isSameText(String s1, String s2) {
        if (isNullOrEmpty(s1)) {
            return isNullOrEmpty(s2);
        }
        return s1.equals(s2);
    }
    // ~check and test

    // convert and cast
    public static boolean isVisibleChar(char ch) {
        switch (ch) {
            case '\0':
            case ' ':
            case '\t':
            case '\r':
            case '\n':
                return false;
        }
        return true;
    }

    /**
     * str的第一个可见字符
     *
     * @param str 待处理的字符串
     * @return '\0'：str中没有可见字符；其他：第一个可见字符
     */
    public static char firstVisibleChar(String str) {
        if (isNullOrEmpty(str) || isNotVisible(str)) {
            return EMPTY_CHAR;
        }
        for (char ch : str.toCharArray()) {
            if (isVisibleChar(ch)) {
                return ch;
            }
        }
        return EMPTY_CHAR;
    }

    /**
     * str的最后一个可见字符
     *
     * @param str 待处理的字符串
     * @return '\0'：str中没有可见字符；其他：最后一个可见字符
     */
    public static char lastVisibleChar(String str) {
        if (isNullOrEmpty(str) || isNotVisible(str)) {
            return EMPTY_CHAR;
        }
        char[] chars = str.toCharArray();
        for (int i = chars.length - 1; i >= 0; i--) {
            char ch = chars[i];
            if (isVisibleChar(ch)) {
                return ch;
            }
        }
        return EMPTY_CHAR;
    }

    public static String firstVisible(String... strings) {
        for (String str : strings) {
            if (isVisible(str)) {
                return str;
            }
        }
        return null;
    }

    public static String format(String template, Object... args) {
        Matcher matcher = placeHolder.matcher(template);
        StringBuilder sb = new StringBuilder();
        while (matcher.find()) {
            for (int i = 1, len = Math.min(matcher.groupCount(), args.length); i <= len; i++) {
                String group = matcher.group(i);
                if (group.equals("<>")) {
                    sb.append(args[i]);
                } else {
                    sb.append(group);
                }
            }
        }
        return sb.toString();
    }

    public static String formatInt(int i, int length) {
        if (length <= 0) {
            return String.valueOf(i);
        }
        return String.format(Locale.US, "%0" + length + "d", i);
    }

    /**
     * 首字母小写的驼峰式 -&gt; 首字母大写的驼峰式
     *
     * @param str 首字母小写的驼峰式
     * @return 首字母大写的驼峰式
     */
    public static String lowerCamelToUpperCamel(String str) {
        Matcher matcher = lowerPrefix.matcher(str);
        if (matcher.find()) {
            String first = matcher.group(1).toUpperCase();
            if (first.length() == str.length()) {
                return first.substring(0, 1).toUpperCase() + str.substring(1);
            }
            return first.toUpperCase() + str.substring(first.length());
        }
        return str;
    }

    /**
     * 首字母大写的驼峰式 -&gt; 首字母小写的驼峰式
     *
     * @param str 首字母大写的驼峰式
     * @return 首字母小写的驼峰式
     */
    public static String upperCamelToLowerCamel(String str) {
        Matcher matcher = upperPrefix.matcher(str);
        if (matcher.find()) {
            String first = matcher.group(1).toUpperCase();
            if (first.length() == str.length()) {
                return first.substring(0, 1).toLowerCase() + str.substring(1);
            }
            return first.toLowerCase() + str.substring(first.length());
        }
        return str;
    }

    public static String getterToField(String getter) {
        if (getterReg.matcher(getter).matches()) {
            String field = getter.substring(2);
            char ch = field.charAt(0);
            if (ch >= 'A' && ch <= 'Z') { // isXXX
                Matcher matcher = upperPrefix.matcher(getter);
                if (matcher.find()) {
                    String first = matcher.group(1);
                    field = first.toLowerCase() + field.substring(first.length());
                }
            } else if (ch == 't') { // getXXX
                field = getter.substring(3);
                Matcher matcher = upperPrefix.matcher(getter);
                if (matcher.find()) {
                    String first = matcher.group(1);
                    field = first.toLowerCase() + field.substring(first.length());
                }
            }
            return field;
        }
        return null;
    }

    public static String fieldToGetter(String field) {
        String getter = "get" + Character.toUpperCase(field.charAt(0));
        if (field.length() > 1) {
            getter += field.substring(1);
        }
        return getter;
    }

    public static String fieldToIsGetter(String field) {
        String getter = "is" + Character.toUpperCase(field.charAt(0));
        if (field.length() > 1) {
            getter += field.substring(1);
        }
        return getter;
    }

    public static String setterToField(String setter) {
        if (setterReg.matcher(setter).matches()) {
            String field = setter.substring(3);
            Matcher matcher = upperPrefix.matcher(setter);
            if (matcher.find()) {
                String first = matcher.group(1);
                field = first.toLowerCase() + field.substring(first.length());
            }
            return field;
        }
        return null;
    }

    public static String fieldToSetter(String field) {
        String setter = "set" + Character.toUpperCase(field.charAt(0));
        if (field.length() > 1) {
            setter += field.substring(1);
        }
        return setter;
    }
    // ~convert and cast

    // join and split
    public static String repeat(String str, int times) {
        if (isNullOrEmpty(str) || times < 2) {
            return str;
        }
        StringBuilder sb = new StringBuilder(str);
        for (int i = 2; i <= times; i++) {
            sb.append(str);
        }
        return sb.toString();
    }

    public static String repeatToAtLeastLength(String str, int minLength) {
        if (isNullOrEmpty(str) || str.length() >= minLength) {
            return str;
        }
        int unitLen = str.length();
        int times = (minLength + unitLen - 1) / unitLen;
        return repeat(str, times);
    }

    public static String join(char delimiter, String... strings) {
        StringBuilder sb = new StringBuilder();
        boolean first = true;
        for (String item : strings) {
            if (first) {
                first = false;
            } else {
                sb.append(delimiter);
            }
            sb.append(item);
        }
        return sb.toString();
    }

    public static String concat(Collection<String> strings) {
        StringBuilder sb = new StringBuilder();
        for (String str : strings) {
            if (str != null) {
                sb.append(str);
            }
        }
        return sb.toString();
    }

    public static String stripChar(String str, char flag) {
        if (isNullOrEmpty(str) || flag == EMPTY_CHAR) {
            return str;
        }
        StringBuilder sb = new StringBuilder();
        for (char ch : str.toCharArray()) {
            if (ch != flag) {
                sb.append(ch);
            }
        }
        return sb.toString();
    }

    public static List<String> split(String str, char delimiter) {
        if (isNullOrEmpty(str)) {
            return Arrays.asList(str);
        }
        StringBuilder sb = new StringBuilder();
        List<String> rtn = new ArrayList<>();
        for (char ch : str.toCharArray()) {
            if (ch == delimiter) {
                if (sb.length() > 0) {
                    rtn.add(sb.toString());
                }
                sb.delete(0, sb.length());
            } else {
                sb.append(ch);
            }
        }
        if (sb.length() > 0) // maybe has last part
        {
            rtn.add(sb.toString());
        }
        return rtn;
    }

    public static String leftPadding(String str, int length, char padding) {
        int len = str.length();
        if (length <= len) {
            return str;
        }
        int add = length - len;
        if (add <= 0) {
            return str;
        }
        StringBuilder wrap = new StringBuilder(str);
        for (int i = 1; i <= add; i++) {
            wrap.insert(0, padding);
        }
        return wrap.toString();
    }

    public static String rightPadding(String str, int length, char padding) {
        int len = str.length();
        if (length <= len) {
            return str;
        }
        int add = length - len;
        if (add <= 0) {
            return str;
        }
        StringBuilder wrap = new StringBuilder(str);
        for (int i = 1; i <= add; i++) {
            wrap.append(padding);
        }
        return wrap.toString();
    }
    // ~join and split
}